#include <boost/program_options.hpp>
#include <iostream>
#include <string>
#include <cmath>

namespace opt = boost::program_options;
constexpr double pi = 3.14159;

void calculateSectorialArea(double a, double r)
{
    std::cout << "Sectorial area:" << pi * std::pow(r, 2) * a / 360 << '\n';
}

void verifyVariablesMap(const opt::variables_map &vm);

int main(int argc, char *argv[])
{
    // Constructing an options describing variable and giving it a
    // textual description "All options"
    opt::options_description desc("Calculating sectorial area:");

    desc.add_options()  // Returns an easy_to_init object
        ("radius,r",
         opt::value<double>()->required(),
         "Radius length")  // Arg key "radius", "r" in short
        ("angle,a",
         opt::value<double>()->required(),
         "Central angle in degree")          // Arg key "angle", "c" in short
        ("help,h", "produce help message");  // Arg key "help", "h" in short

    // Variable to store our command line arguments
    opt::variables_map vm;

    // Parsing and storing arguments
    opt::store(opt::parse_command_line(argc, argv, desc), vm);

    // Must be called after all the parsing and storing
    opt::notify(vm);

    if (vm.count("help") || vm.size() == 0)
    {
        std::cout << desc << '\n';
        return 1;
    }

    verifyVariablesMap(vm);

    calculateSectorialArea(vm["angle"].as<double>(), vm["radius"].as<double>());

    return 0;
}

#define PV_(k_, t_) std::cout << k_ << t_ << ":"
void verifyVariablesMap(const opt::variables_map &vm)
{
    for (auto it = vm.cbegin(); it != vm.end(); ++it)
    {
        if (it->second.value().empty())
        {
            PV_(it->first, " (empty) ");
        }
        if (vm[it->first].defaulted() || it->second.defaulted())
        {
            PV_(it->first, " (default) ");
        }
        bool is_char;
        try
        {
            boost::any_cast<const char *>(it->second.value());
            is_char = true;
        }
        catch (const boost::bad_any_cast &)
        {
            is_char = false;
        }
        bool is_str;
        try
        {
            boost::any_cast<std::string>(it->second.value());
            is_str = true;
        }
        catch (const boost::bad_any_cast &)
        {
            is_str = false;
        }

        if (it->second.value().type() == typeid(int))
        {
            PV_(it->first, " (int) ");
            std::cout << vm[it->first].as<int>() << std::endl;
        }
        else if (it->second.value().type() == typeid(bool))
        {
            PV_(it->first, " (bool) ");
            std::cout << vm[it->first].as<bool>() << std::endl;
        }
        else if (it->second.value().type() == typeid(double))
        {
            PV_(it->first, " (double) ");
            std::cout << vm[it->first].as<double>() << std::endl;
        }
        else if (is_char)
        {
            PV_(it->first, " (char) ");
            std::cout << vm[it->first].as<const char *>() << std::endl;
        }
        else if (is_str)
        {
            PV_(it->first, " (string) ");
            std::string temp = vm[it->first].as<std::string>();
            if (temp.size())
            {
                std::cout << temp << std::endl;
            }
            else
            {
                std::cout << "true" << std::endl;
            }
        }
        else
        {  // Assumes that the only remainder is vector<string>
            try
            {
                std::vector<std::string> vect =
                    vm[it->first].as<std::vector<std::string> >();
                uint i = 0;
                PV_(it->first, " (vector<string>) \n");
                for (auto oit = vect.cbegin(); oit != vect.end(); oit++, ++i)
                {
                    std::cout << "\r> " << it->first << "[" << i
                              << "]=" << (*oit) << std::endl;
                }
            }
            catch (const boost::bad_any_cast &)
            {
                std::cout << "UnknownType(" << it->second.value().type().name()
                          << ")" << std::endl;
            }
        }
    }
}